﻿using Furion.DatabaseAccessor;
using Furion;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using LimaLims.Application.Service.File.Dto;
using LimaLims.Core.Entity.System;
using LimaLims.Core;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using System.Text;
using System.Web;
using Furion.DatabaseAccessor.Extensions;
using LimaLims.Core.Enum;
using Furion.RemoteRequest.Extensions;
using Mapster;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using StackExchange.Profiling.Internal;

namespace LimaLims.Application.Service.File
{

    /// <summary>
    /// 素材库API
    /// </summary>
    [Route("api")]
    public class SysFileService : ISysFileService, IDynamicApiController, ITransient
    {
        private readonly IRepository<SysFile> _sysFileInfoRep;  // 文件信息表仓储
        private readonly IRepository<SysUser> _sysUserRep;
        private readonly IConfiguration _configuration;

        private readonly string bucketName = "bucketName";

        public SysFileService(IRepository<SysFile> sysFileInfoRep, IConfiguration configuration,IRepository<SysUser> sysUserRep)
        {
            _sysFileInfoRep = sysFileInfoRep;
            _configuration = configuration;
            _sysUserRep = sysUserRep;
        }


        /// <summary>
        /// 删除文件
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpPost("sysFileInfo/delete")]
        public async Task DeleteFileInfo(FileInput input)
        {
            var file = await _sysFileInfoRep.FirstOrDefaultAsync(u => u.Id == input.Id);
            if (file != null)
            {
                await file.DeleteAsync();

                switch (file.FileLocation)
                {
                    //case (int)FileLocation.MINIO:
                    //    await _oSSServiceFactory.Create().RemoveObjectAsync(file.FileBucket, string.Concat(file.FilePath, "/", file.FileObjectName));
                    //    break;

                    //case (int)FileLocation.ALIYUN:
                    //    await _oSSServiceFactory.Create("Aliyun").RemoveObjectAsync(file.FileBucket, string.Concat(file.FilePath, "/", file.FileObjectName));
                    //    break;

                    //case (int)FileLocation.TENCENT:
                    //    await _oSSServiceFactory.Create("QCloud").RemoveObjectAsync(file.FileBucket, string.Concat(file.FilePath, "/", file.FileObjectName));
                    //    break;

                    default:
                        var filePath = Path.Combine(App.WebHostEnvironment.WebRootPath, file.FilePath, file.FileObjectName);
                        if (System.IO.File.Exists(filePath))
                            System.IO.File.Delete(filePath);
                        break;
                }
            }
        }

        /// <summary>
        /// 获取文件详情
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpGet("sysFileInfo/detail")]
        public async Task<SysFile> GetFileInfo([FromQuery] FileInput input)
        {
            var file = await _sysFileInfoRep.FirstOrDefaultAsync(u => u.Id == input.Id);
            if (file == null)
                throw Oops.Oh("文件不存在");
            return file;
        }

        /// <summary>
        /// 预览文件
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpGet("sysFileInfo/preview")]
        public async Task<IActionResult> PreviewFileInfo([FromQuery] FileInput input)
        {
            return await DownloadFileInfo(input);
        }

        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="file"></param>
        /// <param name="groupId">分组ID</param>
        /// <returns></returns>
        [HttpPost("sysFileInfo/upload")]
        public async Task<FileOutput> UploadFileDefault(IFormFile file, long groupId)
        {
            if (string.IsNullOrWhiteSpace(groupId.ToString())) throw Oops.Oh("请选择分组!");
            //对象存储的key
            const string key = "UploadFile:Default";
            // 可以读取系统配置来决定将文件存储到什么地方
            return await UploadFile(file, groupId, key, FileLocation.LOCAL);
        }

        /// <summary>
        /// 上传模板文件
        /// </summary>
        /// <returns></returns>
        [HttpPost("sysFileInfo/uploadPrintFile")]
        public async Task<FileOutput> UploadPrintFile(IFormFile file, long groupId)
        {
            const string key = "UploadFile:PrintTemplate";
            // 可以读取系统配置来决定将文件存储到什么地方
            return await UploadFile(file, groupId, key, FileLocation.LOCAL);
        }

        /// <summary>
        /// 上传视频文件
        /// </summary>
        /// <returns></returns>
        [HttpPost("sysFileInfo/uploadVideo")]
        public async Task<FileOutput> UploadVideo(IFormFile file, long groupId)
        {
            const string key = "UploadFile:Video";
            // 可以读取系统配置来决定将文件存储到什么地方
            return await UploadFile(file, groupId, key, FileLocation.LOCAL);
        }

        /// <summary>
        /// 下载文件
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpGet("sysFileInfo/download")]
        [AllowAnonymous]
        public async Task<IActionResult> DownloadFileInfo([FromQuery] FileInput input)
        {
            var file = await GetFileInfo(input);
            var fileName = HttpUtility.UrlEncode(file.FileOriginName, Encoding.GetEncoding("UTF-8"));

            switch (file.FileLocation)
            {
                //case (int)FileLocation.ALIYUN:
                //    var filePath = string.Concat(file.FilePath, "/", file.FileObjectName);
                //    var (stream1, _) = await (await _oSSServiceFactory.Create("Aliyun").PresignedGetObjectAsync(bucketName, filePath, 5)).GetAsStreamAsync();
                //    return new FileStreamResult(stream1, "application/octet-stream") { FileDownloadName = fileName };

                //case (int)FileLocation.TENCENT:
                //    var filePath1 = string.Concat(file.FilePath, "/", file.FileObjectName);
                //    var (stream2, _) = await (await _oSSServiceFactory.Create("QCloud").PresignedGetObjectAsync(bucketName, filePath1, 5)).GetAsStreamAsync();
                //    return new FileStreamResult(stream2, "application/octet-stream") { FileDownloadName = fileName };

                //case (int)FileLocation.MINIO:
                //    var filePath2 = string.Concat(file.FilePath, "/", file.FileObjectName);
                //    var (stream3, _) = await (await _oSSServiceFactory.Create().PresignedGetObjectAsync(file.FileBucket, filePath2, 5)).GetAsStreamAsync();
                //    return new FileStreamResult(stream3, "application/octet-stream") { FileDownloadName = fileName };

                default:
                    var filePath4 = Path.Combine(file.FilePath, file.FileObjectName);
                    var path = Path.Combine(App.WebHostEnvironment.WebRootPath, filePath4);
                    return new FileStreamResult(new FileStream(path, FileMode.Open), "application/octet-stream") { FileDownloadName = fileName };
            }
        }

        /// <summary>
        /// 上传头像
        /// </summary>
        /// <param name="file"></param>
        /// <param name="groupId"></param>
        /// <returns></returns>
        [DisplayName("上传头像")]
        [HttpPost("sysFileInfo/uploadAvatar")]
        public async Task<FileOutput> UploadFileAvatar([Required] IFormFile file, long groupId)
        {
            var user = await _sysUserRep.FirstOrDefaultAsync(u => u.Id == CurrentUserInfo.UserId);
            // 删除当前用户已有头像
            if (!string.IsNullOrWhiteSpace(user.Avatar))
            {
                var fileId = Path.GetFileNameWithoutExtension(user.Avatar);
                if(fileId!= "undefined")
                await DeleteFileInfo(new FileInput { Id = long.Parse(fileId) });
            }
            var res = await UploadFile(file, groupId, "UploadFile:Avatar");
            user.Avatar = $"{res.FilePath}/{res.FileObjectName}";
            await _sysUserRep.UpdateIncludeAsync(user, new[] { nameof(SysUser.Avatar) }, true);
            return res;
        }



        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="file"></param>
        /// <param name="groupId"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        [NonAction]
        public async Task<FileOutput> UploadFile(IFormFile file, long groupId, string key)
        {
            string path = _configuration[$"{key}:path"];
            string date = DateTime.Now.ToString("yyyyMMdd");
            string pathtenant = string.Format("{0}/{1}/{2}", path, CurrentUserInfo.TenantId, date);
            var filePath = Path.Combine(App.WebHostEnvironment.WebRootPath, pathtenant);
            if (!Directory.Exists(filePath))
                Directory.CreateDirectory(filePath);
            var allowContentType = _configuration.GetSection($"{key}:contentType").Get<IEnumerable<string>>();
            if (!allowContentType.Contains(file.ContentType)) throw Oops.Oh("不允许的文件类型");
            var allowMaxSize = long.Parse(_configuration[$"{key}:maxSize"]);
            var fileSizeKb = (long)(file.Length / 1024.0); // 文件大小KB
            if (fileSizeKb > allowMaxSize) throw Oops.Oh("文件超过允许大小");
            var originalFilename = file.FileName; // 文件原始名称
            var fileSuffix = Path.GetExtension(file.FileName).ToLower(); // 文件后缀

            // 先存库获取Id
            var newFile = await new SysFile
            {
                FileLocation = (int)FileLocation.LOCAL,
                FileBucket = FileLocation.LOCAL.ToString(),
                FileOriginName = originalFilename,
                FileSuffix = fileSuffix.TrimStart('.'),
                FileSizeKb = fileSizeKb.ToString(),
                FilePath = pathtenant,
                SysFileGroupId = groupId
            }.InsertNowAsync();

            var finalName = newFile.Entity.Id + fileSuffix; // 生成文件的最终名称
            using (var stream = System.IO.File.Create(Path.Combine(filePath, finalName)))
            {
                await file.CopyToAsync(stream);
            }
            newFile.Entity.FileObjectName = finalName;
            var fileOutput = newFile.Entity.Adapt<FileOutput>();
            return fileOutput; // 返回文件
        }

        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="file">文件</param>
        /// <param name="groupId">分组编码</param>
        /// <param name="key">对象存储键</param>
        /// <param name="fileLocation">文件存储位置</param>
        /// <returns></returns>
        private async Task<FileOutput> UploadFile(IFormFile file, long groupId, string key, FileLocation fileLocation)
        {
            string path = _configuration[$"{key}:path"];
            string date = DateTime.Now.ToString("yyyyMMdd");
            string pathtenant = string.Format("{0}/{1}/{2}", path, CurrentUserInfo.TenantId, date);
            var allowContentType = _configuration.GetSection($"{key}:contentType").Get<IEnumerable<string>>();
            if (!allowContentType.Contains(file.ContentType)) throw Oops.Oh("不允许的文件类型");
            var allowMaxSize = long.Parse(_configuration[$"{key}:maxSize"]);
            var fileSizeKb = (long)(file.Length / 1024.0); // 文件大小KB
            if (fileSizeKb > allowMaxSize) throw Oops.Oh("文件超过允许大小");

            var originalFilename = file.FileName; // 文件原始名称
            var fileSuffix = Path.GetExtension(file.FileName).ToLower(); // 文件后缀

            // 先存库获取Id
            var newFile = await new SysFile
            {
                FileLocation = (int)fileLocation,
                FileBucket = bucketName,
                FileOriginName = originalFilename,
                FileSuffix = fileSuffix.TrimStart('.'),
                FileSizeKb = fileSizeKb.ToString(),
                FilePath = pathtenant,
                SysFileGroupId = groupId,
            }.InsertNowAsync();
            var finalName = newFile.Entity.Id + fileSuffix; // 生成文件的最终名称
            newFile.Entity.FileObjectName = finalName;
            switch (fileLocation)
            {
                //case FileLocation.ALIYUN:
                //    var filePath = string.Concat(path, "/", finalName);
                //    var stream = file.OpenReadStream();
                //    await _oSSServiceFactory.Create("aliyun").PutObjectAsync(bucketName, filePath, stream);
                //    break;

                //case FileLocation.TENCENT:
                //    var filePath1 = string.Concat(path, "/", finalName);
                //    var stream1 = file.OpenReadStream();
                //    await _oSSServiceFactory.Create("qcloud").PutObjectAsync(bucketName, filePath1, stream1);
                //    break;

                //case FileLocation.MINIO:
                //    var filePath2 = string.Concat(path, "/", finalName);
                //    var stream2 = file.OpenReadStream();
                //    await _oSSServiceFactory.Create().PutObjectAsync(bucketName, filePath2, stream2);
                //    break;

                default:
                    var filePath4 = Path.Combine(App.WebHostEnvironment.WebRootPath, pathtenant);
                    if (!Directory.Exists(filePath4))
                        Directory.CreateDirectory(filePath4);
                    using (var stream4 = System.IO.File.Create(Path.Combine(filePath4, finalName)))
                    {
                        await file.CopyToAsync(stream4);
                    }
                    break;
            }
            var fileOutput = newFile.Entity.Adapt<FileOutput>();
            return fileOutput; // 返回文件
        }

        /// <summary>
        /// 获取素材
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [DisplayName("获取素材")]
        [HttpGet("sysFileInfo/page")]
        public async Task<PageResult<FileOutput>> QueryPageList([FromQuery] FilePageInput input)
        {
            var article = await _sysFileInfoRep.DetachedEntities
                                         .Where(u => u.ValidStatus).OrderBy(u => u.Sort)
                                         .Where(input.SysFileGroupId.HasValue, u => u.SysFileGroupId == input.SysFileGroupId)
                                         .Select(x => x.Adapt<FileOutput>())
                                         .ToADPagedListAsync(input.PageNo, input.PageSize);
            return article;
        }
    }
}
